#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<vector>
#include<functional>
#include<stdio.h>

using namespace std;

// 自定义信号处理函数（递达时执行）
void sigint_handler(int sig) {
    cout << "SIGINT（信号2）递达！执行自定义处理动作" << endl;
}

int main() 
{
    // 1. 注册SIGINT的处理动作：自定义handler（相当于快递处理方式设为“拆快递”）
    signal(SIGINT, sigint_handler);

    // 2. 阻塞SIGINT信号（相当于给快递柜设“拒收SIGINT快递”规则）
    sigset_t mask, oldmask;
    sigemptyset(&mask);       // 初始化信号集
    sigaddset(&mask, SIGINT); // 将SIGINT加入阻塞集
    sigprocmask(SIG_BLOCK, &mask, &oldmask); // 开启阻塞，保存旧的阻塞集

    cout << "已阻塞SIGINT，现在发送3次SIGINT（用kill -2 " << getpid() << "）" << endl;
    sleep(20); // 这20秒内，手动在另一个终端执行3次：kill -2 进程PID（产生3次SIGINT）

    // 3. 查看SIGINT的未决状态（可选，验证未决位是否为1）
    sigset_t pending;
    sigpending(&pending);
    if (sigismember(&pending, SIGINT)) {
        cout << "解除阻塞前，SIGINT处于未决态（常规信号只记1次）" << endl;
    }

    // 4. 解除SIGINT的阻塞（相当于取消“拒收”规则）
    sleep(5);
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    cout << "已解除SIGINT阻塞，等待信号递达..." << endl;

    sleep(5); // 等待递达完成
    return 0;
}
// 要搞懂这段代码，核心是理解 sigset_t 信号集 + 4 个核心函数（sigemptyset/sigaddset/sigprocmask/sigpending）的分工，它们共同完成「信号阻塞→验证未决→解除阻塞」的流程。下面从「函数用法→代码机制→实际效果」三层拆解，结合之前的 “快递类比” 让你彻底明白。
// 一、先明确核心工具：sigset_t 信号集
// sigset_t 是 Linux 定义的「信号集合类型」，本质是一个 bitmask（位掩码）—— 用每一个 bit 位表示一个信号的 “是否存在”（比如 bit2 对应 SIGINT 信号，bit3 对应 SIGQUIT 信号）。
// 比如 sigset_t mask 中，若 bit2 为 1，表示集合中包含 SIGINT 信号；若为 0，表示不包含；
// 所有信号相关的阻塞、未决判断，都基于这个 “信号集” 进行（内核用它管理进程的阻塞集、未决集）。
// 二、4 个核心函数的用法与作用（逐个拆解）
// 这段代码的核心是「修改进程的阻塞集」，4 个函数各司其职，我们按代码执行顺序讲解：
// 1. sigemptyset(&mask)：初始化信号集为空
// 函数原型：int sigemptyset(sigset_t *set);
// 作用：把信号集 set 中的所有 bit 位设为 0 → 表示这个集合中 “没有任何信号”；
// 为什么要做：C/C++ 中未初始化的变量值是随机的，sigset_t 也不例外。如果不初始化，可能包含随机信号，导致后续阻塞逻辑出错；
// 类比：准备一个空白的 “快递拒收清单”，一开始上面没有任何快递公司。
// 2. sigaddset(&mask, SIGINT)：向信号集添加信号
// 函数原型：int sigaddset(sigset_t *set, int signum);
// 作用：把信号 signum 对应的 bit 位设为 1 → 表示将该信号加入集合；
// 参数：&mask 是目标信号集，SIGINT 是要添加的信号（编号 2）；
// 类比：在 “快递拒收清单” 上添加 “顺丰快递”（对应 SIGINT 信号），后续要拒收这类快递。
// 3. sigprocmask(SIG_BLOCK, &mask, &oldmask)：修改进程的阻塞集
// 这是最核心的函数！作用是 修改当前进程的内核阻塞集（PCB 中的 block 标志），进程的阻塞状态由它决定。
// 函数原型：int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
// 三个参数的关键作用（重点！）：
// 参数    取值 / 含义    本代码中的作用
// how    修改方式（3 种可选）    这里用 SIG_BLOCK → 「新增阻塞」：将 set 中的信号添加到进程的阻塞集中（原有阻塞信号保留）
// set    要操作的信号集    这里是 &mask（包含 SIGINT）→ 要新增阻塞的信号是 SIGINT
// oldset    输出参数（保存旧阻塞集）    这里是 &oldmask → 把进程 “修改前的原始阻塞集” 保存到 oldmask 中（后续用于恢复）
// 补充：how 的三种取值（常用）：
// how 取值    效果    场景
// SIG_BLOCK    新增阻塞：进程阻塞集 = 原有阻塞集 ∪ set 中的信号    临时添加某个信号的阻塞（如本代码）
// SIG_UNBLOCK    解除阻塞：进程阻塞集 = 原有阻塞集 ∩ （~set）    只解除 set 中信号的阻塞
// SIG_SETMASK    替换阻塞集：进程阻塞集 = set 中的信号    完全覆盖原有阻塞规则（如本代码后续恢复阻塞集）
// 类比：告诉快递柜 “按这个清单（mask）新增拒收规则”，同时把 “原来的拒收清单”（oldmask）抄下来，方便之后恢复。
// 4. sigpending(&pending) + sigismember(&pending, SIGINT)：查看未决信号
// 先看 sigpending(&pending)：
// 函数原型：int sigpending(sigset_t *set);
// 作用：内核把当前进程的「未决信号集」（PCB 中的 pending 标志）拷贝到 set 中 → 让用户态程序能查看 “哪些信号已产生，但被阻塞未递达”；
// 类比：查看快递柜里 “已送达但因拒收规则没取的快递清单”。
// 再看 sigismember(&pending, SIGINT)：
// 函数原型：int sigismember(const sigset_t *set, int signum);
// 作用：判断信号 signum 是否在信号集 set 中（对应 bit 位是否为 1）；
// 返回值：1 = 存在，0 = 不存在，-1 = 错误；
// 本代码中：判断 SIGINT 是否在「未决信号集」中 → 验证 “之前发送的 SIGINT 是不是被阻塞在未决态”。
// 5. 后续的 sigprocmask(SIG_SETMASK, &oldmask, NULL)：恢复原始阻塞集
// 作用：用之前保存的 oldmask（修改前的原始阻塞集）替换当前进程的阻塞集 → 解除对 SIGINT 的阻塞（因为原始阻塞集中没有 SIGINT）；
// 类比：快递柜恢复 “原来的拒收清单”，之前被拒收的 SIGINT 快递（未决态）现在可以取了（信号递达）。
// 三、这段代码的完整机制（结合 “快递类比” 走一遍流程）
// 我们把代码和 “快递场景” 完全对应，让你看清每一步的本质：
// 代码执行流程 + 内核状态变化：
// 初始化信号集：
// cpp
// 运行
// sigset_t mask, oldmask;
// sigemptyset(&mask);       // mask = 空集（bit全0）
// sigaddset(&mask, SIGINT); // mask 的 bit2（SIGINT）设为1 → mask = {SIGINT}
// 类比：准备一个空白拒收清单，然后添加 “顺丰快递”（SIGINT）。
// 新增 SIGINT 阻塞：
// cpp
// 运行
// sigprocmask(SIG_BLOCK, &mask, &oldmask);
// 内核动作：进程的阻塞集 = 原有阻塞集 + SIGINT → 从此进程会阻塞 SIGINT 信号；
// 保存 oldmask：把 “没有阻塞 SIGINT” 的原始阻塞集存起来（后续恢复用）；
// 类比：快递柜新增 “拒收顺丰” 规则，同时抄下原来的清单（原来不拒收任何快递）。
// 等待并发送 SIGINT 信号：
// cpp
// 运行
// cout << "已阻塞SIGINT，现在发送3次SIGINT（用kill -2 " << getpid() << "）" << endl;
// sleep(10); // 10秒内手动发送3次 kill -2 进程PID
// 关键现象：这 3 次 SIGINT 信号会被内核 “接收”，但因为进程阻塞 SIGINT → 内核设置 SIGINT 的「未决位 = 1」（未决态）；
// 常规信号特性：3 次重复发送只计 1 次未决（bit2 只能是 1，无法记录次数）；
// 类比：10 秒内连续寄 3 个顺丰快递，快递柜都收下了，但因为拒收规则，都存放在柜里（未决态），且只显示 “有 1 个顺丰快递待取”（不记录 3 次）。
// 验证未决状态：
// cpp
// 运行
// sigset_t pending;
// sigpending(&pending); // 拷贝内核的未决集到 pending
// if (sigismember(&pending, SIGINT)) {
//     cout << "解除阻塞前，SIGINT处于未决态（常规信号只记1次）" << endl;
// }
// 内核动作：pending 中包含 SIGINT（bit2=1）→ sigismember 返回 1 → 打印提示；
// 类比：查看快递柜清单，发现 “有 1 个顺丰快递待取”（未决态）。
// 解除阻塞，恢复原始状态：
// cpp
// 运行
// sigprocmask(SIG_SETMASK, &oldmask, NULL);
// 内核动作：用 oldmask（原始阻塞集，不含 SIGINT）替换当前阻塞集 → 进程不再阻塞 SIGINT；
// 连锁反应：SIGINT 信号的「未决位 = 1」且「阻塞位 = 0」→ 内核立即递达 SIGINT 信号，执行处理动作（本代码中是自定义 handler）；
// 递达后：内核清除 SIGINT 的未决位（bit2=0）；
// 类比：快递柜恢复原来的规则（不拒收顺丰），柜里的顺丰快递被取出（信号递达），清单上该快递消失。

// int main()
// {
//     if(fork() == 0)
//     {
//         sleep(1);
//         int a = 10;
//         a /= 0;
//         exit(0); 
//     }

//     int status = 0;
//     waitpid(-1, &status, 0);

//     printf("exit signal: %d, core dump: %d\n", status&0x7F, (status>>7) & 1);

//     return 0;
// }

// void handler(int sig)
// {
//     printf("catch a sig: %d\n", sig);
// }

// int main()
// {
//     //signal(SIGSEGV, handler);
//     sleep(1);          // 进程睡眠1秒（无关错误，只是延迟了错误触发）
//     int *p = NULL;     // 定义指针p，赋值为NULL（空指针）
//     *p = 100;          // 错误核心：试图给p指向的内存赋值100
//     while(1);          // 死循环（永远执行不到，因为上一行已经触发错误）

//     return 0;
// }
// 1. 进程执行 *p = 100 → 触发非法内存访问
// 2. 内核检测到错误 → 向进程发送 SIGSEGV 信号（11）
// 3. 进程暂停当前执行 → 跳去执行你注册的 handler 函数（打印 "catch a sig: 11"）
// 4. handler 执行完毕 → 进程回到「触发错误的指令（*p = 100）」继续执行
// 5. 再次执行 *p = 100 → 再次触发非法访问，重复步骤2-4
// ￼
// 如此循环往复，就会看到终端不断打印 catch a sig: 11，看起来像 “循环打印”。
// 二、关键原因：SIGSEGV 信号的 “默认恢复行为”
// 为什么 handler 执行完后，进程会回到错误指令继续执行？这和信号的「恢复机制」有关：
// • 对于大多数信号（如 SIGINT、SIGALRM），处理函数执行完后，进程会回到「被信号中断的下一条指令」继续执行；
// • 但 SIGSEGV 是「致命错误信号」，触发它的指令是「非法指令」（访问无效内存）—— 内核没有办法 “修复” 这个错误，
// 所以只能回到「触发错误的原指令」重新执行；
// • 重新执行后，错误依然存在，会再次触发 SIGSEGV，形成死循环。
// 简单说：SIGSEGV 是 “因错误指令产生的信号”，信号处理函数只能打印信息，
// 无法修复错误指令，所以进程会反复执行错误指令，反复触发信号。
// 三、为什么之前没注册 handler 时，不会循环？
// 之前你没写 signal(SIGSEGV, handler) 时，SIGSEGV 会使用「默认行为」—— 
// 终止进程 + 生成 core 文件。所以只会输出一次 Segmentation fault (core dumped)，进程直接退出，不会循环。
// 而注册了自定义 handler 后，你改变了 SIGSEGV 的处理动作（从 “终止进程” 
// 变成 “执行 handler 后继续执行”），但没有解决「错误指令存在」的根本问题，所以才会循环。


// struct task_struct
// {
//     pid_t id;
//     int count = 10; // 时间片，本质就是一个计数器！
//     void(*code)();
// }t;

// //////////func////////////
// void Sched()
// {
//     std::cout << "我是进程调度" << std::endl;
//     // t.count--;
//     // if(t.count <= 0)
//     // // 切换其他进程
// }
// void MemManger()
// {
//     std::cout << "我是周期性的内存管理，正在检查有没有内存问题" << std::endl;
// }
// void Fflush()
// {
//     std::cout << "我是刷新程序，我在定期刷新内存数据，到磁盘" << std::endl;
// }
// /////////////////////////
// using func_t = std::function<void()>;

// int gcount = 0;
// std::vector<func_t> gfuncs;

// //将信号转换成硬件中断
// void handler(int signo)
// {
//     for(auto &f : gfuncs)
//     {
//         f();
//     }
//     std::cout << "gcount: " << gcount << std::endl;
//     int n = alarm(1);//重设闹钟，返回上一次闹钟剩余时间
//     std::cout << "剩余时间：" <<  n << std::endl; 
// }

// int main()
// {
//     gfuncs.push_back(Sched);
//     gfuncs.push_back(MemManger);
//     gfuncs.push_back(Fflush);

//     alarm(1);
//     signal(SIGALRM, handler);

//     while(true)
//     {
//         pause();//阻塞进程
//         std::cout << "我醒来了..." << std::endl;
//         gcount++;
//     }
// }



// void handler(int signumber)
// {
//     std::cout << "我是:" << getpid() << ", 我获得了一个信号：" << signumber << std::endl; 
// }

// int main()
// {
//     signal(SIGABRT, handler);
//     while(true)
//     {
//         sleep(1);
//         //raise(2);//函数给自己发信号
//          abort();abort() //的作用是 强制触发 SIGABRT 信号，让进程终止）。
// // 但这里有个关键：abort() 触发的 SIGABRT，即使你注册了自定义处理函数，
// // 进程最终也会被终止（SIGABRT 是 “终止级信号”，自定义处理函数只是
// //  “临终前执行一次”，不能阻止进程退出）。
//     }
// }

// int main()
// {
//     std::cout << "我是进程:" << getpid() << std::endl; 
//     // signal(SIGINT, handler);
//     // signal(2, handler);
//     // signal(2, SIG_IGN);//设置忽略信号的宏
//     // signal(2,SIG_DFL);//设置默认处理的宏

//         //20号信号
//     // signal(SIGTSTP, handler);
//     while(true)
//     {
//         std::cout << "I am a process, I am waiting a signal!" << getpid() << std::endl;
//         sleep(1);
//     }
//     return 0;
// }

// int main()
// {
//     while(true)
//     {
//         std::cout << "I am a process, I am waiting a signal!" << std::endl;
//         sleep(1);
//     }
//     return 0;
// }